<template>
	<view class="page">
		<u-navbar title="添加音频" back-icon-color="#fff" title-color="#fff" :background="background"></u-navbar>
		<view class="top section" >
			<view class="title">您可以在录音时阅读此内容...</view>
			<view class="inpcon">
				<textarea :value="inputValue" placeholder="粘贴你的内容" placeholder-class="font" class=" textarea-class"
					maxlength="1000" />
			</view>
		</view>
		<view class="bottom">
			<view class="ripple">
				<view class="min" >
					<view v-if="recordStatus == '已结束' && audioUrl !=''">
						<image @click="playStart" v-if="!isPlay" class="image_3"
							src="https://terminator.axu9.com/images/paly-icon.png" />
						<image @click="playStart" v-else class="image_3"
							src="https://terminator.axu9.com/images/pause-audio.png" />
					</view>
					<text>{{recpowert}}</text>
				</view>
				<canvas v-if="recordStatus !== '已结束'" type="2d" class="recwave-Histogram3"></canvas>
				
				<view v-if="recordStatus == '已结束'" style="height: 100%;display: flex;align-items: center;">
					<image v-if="isPlay" class="image_4 image_4gif" src="https://terminator.axu9.com/images/paly-icon-gif.gif" />
					<image v-else class="image_4" src="https://terminator.axu9.com/images/audip-yinpin.png" />
				</view>
				
				
			</view>
			<view class="b_btn">
				<view class="btnall">
					<view class="btn_le" >
						<image v-if="recordStatus == '已结束' || recordStatus == '已暂停'" @click="recStopX()"  class="image_6"
							src="https://terminator.axu9.com/images/addaudio-delicon.png" />
					</view>
					<view class="btn_con">
						<image v-if="recordStatus == '未开始' || recordStatus == '已结束'"  @click="startRec()" class="image_5"
							src="https://terminator.axu9.com/images/start-record.png" />
						<image v-if="recordStatus == '已暂停'"  @click="recResume()" class="image_5"
							src="https://terminator.axu9.com/images/start-record.png" />
						<image v-if="recordStatus == '录音中'" @click="recPause()" class="image_5"
							src="https://terminator.axu9.com/images/record-pause.png" />
					</view>
					<view class="btn_ri">
						<image v-if="recordStatus == '已暂停'" @click="recStop()" class="image_6 image_7"
								src="https://terminator.axu9.com/images/addaudio-ok.png" />
						<image v-if="recordStatus == '已结束'" @click="addAudio" class="image_6 image_7"
								src="https://terminator.axu9.com/images/add-record-icon.png" />
					</view>
				</view>
				<view class="btn_text" v-if="recordStatus == '未开始' || recordStatus == '已结束'">点击开始</view>
				<view class="btn_text" v-if="recordStatus == '已暂停'">点击继续</view>
				<view class="btn_text" v-if="recordStatus == '录音中'">点击暂停</view>
			</view>
		</view>
		
		
		<TestPlayer ref="player" @getUrl="getUrl" v-show="false"/>
		<!-- 设置标题弹窗 -->
		<audioTitlePop ref="audiotitle" @submit="submit" :isLoading="isLoading"></audioTitlePop>
	</view>
</template>

<script>
	/**这里是逻辑层**/
	
	/** 先引入Recorder （ 需先 npm install recorder-core ）**/
	import Recorder from 'recorder-core'; //注意如果未引用Recorder变量，可能编译时会被优化删除（如vue3 tree-shaking），请改成 import 'recorder-core'，或随便调用一下 Recorder.a=1 保证强引用
	
	/** H5、小程序环境中：引入需要的格式编码器、可视化插件，App环境中在renderjs中引入 **/
	// 注意：如果App中需要在逻辑层中调用Recorder的编码/转码功能，需要去掉此条件编译，否则会报未加载编码器的错误
	// #ifdef H5 || MP-WEIXIN
	//按需引入需要的录音格式编码器，用不到的不需要引入，减少程序体积；H5、renderjs中可以把编码器放到static文件夹里面用动态创建script来引入，免得这些文件太大
	import 'recorder-core/src/engine/mp3.js'
	import 'recorder-core/src/engine/mp3-engine.js'
	// import 'recorder-core/src/engine/wav.js'
	// import 'recorder-core/src/engine/pcm.js'
	// import 'recorder-core/src/engine/g711x'
	
	//可选引入可视化插件
	import 'recorder-core/src/extensions/waveview.js'
	import 'recorder-core/src/extensions/wavesurfer.view.js'
	
	import 'recorder-core/src/extensions/frequency.histogram.view.js'
	import 'recorder-core/src/extensions/lib.fft.js'
	
	//实时播放语音，仅支持h5
	import 'recorder-core/src/extensions/buffer_stream.player.js'
	//测试用根据简谱生成一段音乐
	// import 'recorder-core/src/extensions/create-audio.nmn2pcm.js'
	// #endif
	
	/** 引入RecordApp **/
	import RecordApp from 'recorder-core/src/app-support/app.js'
	//【所有平台必须引入】uni-app支持文件
	import '../../uni_modules/Recorder-UniCore/app-uni-support.js'
	
	var disableOgg = false;
	// #ifdef MP-WEIXIN
	//可选引入微信小程序支持文件
	import 'recorder-core/src/app-support/app-miniProgram-wx-support.js'
	disableOgg = true; //小程序不测试ogg js文件太大
	// #endif
	
	
	// #ifdef H5 || MP-WEIXIN
	//H5、renderjs中可以把编码器放到static文件夹里面用动态创建script来引入，免得这些文件太大
	import 'recorder-core/src/engine/beta-amr'
	import 'recorder-core/src/engine/beta-amr-engine'
	// #endif
	// // #ifdef H5
	// 	//app、h5测试ogg，小程序不测试ogg js文件太大
	// 	import 'recorder-core/src/engine/beta-ogg'
	// 	import 'recorder-core/src/engine/beta-ogg-engine'
	// // #endif
	RecordApp.UniNativeUtsPlugin = {
		nativePlugin: true
	}; //目前仅支持原生插件，uts插件不可用
	import TestPlayer from './test_player___.vue'; //手撸的一个跨平台播放器
	import audioTitlePop from "../../components/audioTitlePop"
	let innerAudioContext = null

	import {
		prompt,
		tapeFile,
		myYinseFileup,
		myYinseFileupNew,
		morenShengYin,
		MinorlanguagesTiqu//提取音色
	} from "@/api/audio.js"
	export default {
		components:{
			TestPlayer,
			audioTitlePop
		},
		data(){
			return{
				recType: "mp3", //类型mp3 wav
				recSampleRate: 48000, //采样率--原有16000  32000 48000
				recBitRate: 256, //比特率--原有16  92
				recwaveChoiceKey:"Histogram3",
				
				takeoffEncodeChunkSet: false,
				takeoffEncodeChunkMsg: "",
				useAEC: false,
				appUseH5Rec: false,
				showUpload: false,
				
				recpowerx: 0,
				recpowert: "0:00",
				
				rippleHei:0,
				rippleWid:0,
				
				audioUrl:"",//音频路径
				inputValue:"",//模板内容
				isPlay:false,//是否在播放
				recordStatus:"未开始",//播放状态 未开始 录音中 已暂停 已结束
				isLoading: false, //是否加载中
				audioTitle:"",//标题
				toAddType: 1, //2 AI录制
				clickoption: 1, //
				toOptionType: 1, //
				videoPath: '', //视频路径
				timbreId: '', //音色id
				editTribreErrNum: 0,//修改音色第二部失败次数
				isxiao:false,//是否为小语种
				list_id:""
			}
		},
		mounted() {
			this.isMounted = true;
			this.uniPage__onShow(); //onShow可能比mounted先执行，页面准备好了时再执行一次
		
			//可选，立即显示出环境信息
			this.reclog("正在执行Install，请勿操作...", "#f60");
			RecordApp.Install(() => {
				this.reclog("Install成功，环境：" + this.currentKeyTag(), 2);
				this.reclog("请先请求录音权限，然后再开始录音");
				this.reclog("然后再开始录音");
				
			}, (err) => {
				this.reclog("RecordApp.Install出错：" + err, 1);
			});
		},
		onShow() { //当组件用时没这个回调
			if (this.isMounted) this.uniPage__onShow(); //onShow可能比mounted先执行，页面可能还未准备好
			const query = uni.createSelectorQuery().in(this);
			query
				.select(".ripple")
				.boundingClientRect((data) => {
					if (data) {
						this.rippleWid = data.width
						this.rippleHei = data.height
					}
				})
				.exec();
		},
		onLoad(options) {
			console.log(options, 'options')
			this.toAddType = options.toAddType ? options.toAddType : 1;
			this.clickoption = options.clickoption ? options.clickoption : 1;
			this.toOptionType = options.toOptionType ? options.toOptionType : 1;
			this.inputValue = options.wenan ? options.wenan : '';
			this.videoPath = options.videoPath ? options.videoPath : '';
			this.timbreId = options.timbreId ? options.timbreId : '';
			this.isxiao = options.isxiao == true || options.isxiao == 'true' ? options.isxiao : false
			this.list_id = options.listid ? options.listid : ""
			if (this.toAddType != 2 && this.toAddType != 5 && this.toAddType != 4) {
				this.getText();
			}
			innerAudioContext = uni.createInnerAudioContext();
			innerAudioContext.autoplay = false;
			innerAudioContext.loop = false;
			innerAudioContext.volume = 1;
			
			innerAudioContext.onPlay(() => {
				this.isPlay = true
			});
			innerAudioContext.onPause(() => {
				this.isPlay = false
			});
			innerAudioContext.onEnded(res=>{
				this.isPlay = false
			})
			innerAudioContext.onStop(res=>{
				this.isPlay = false
			})
			innerAudioContext.onError((res) => {
				this.isPlay = false
				uni.showToast({
					title: '加载失败',
					icon: 'none'
				})
			});
		},
		/*#ifdef VUE3*/
		unmounted() /*#endif*/ /*#ifndef VUE3*/ destroyed() /*#endif*/ {
			RecordApp.Stop(); //清理资源，如果打开了录音没有关闭，这里将会进行关闭
		},
		onHide() {
			innerAudioContext.stop()
		},
		onUnload() {
			innerAudioContext.stop()
			innerAudioContext.destroy()
		},
		methods:{
			// 制作音色提示文字
			getText() {
				prompt().then(res => {
					console.log(res, 'eeeeee')
					this.inputValue = res.data;
				}).catch(err => {
			
				})
			},
			// 添加音频
			addAudio(){
				if(this.audioUrl == "")return
				this.isPlay = false
				innerAudioContext.stop()
				if(this.isxiao){
					this.uplodyinse(this.audioUrl)
					return
				}
				this.$refs.audiotitle.open();
			},
			getUrl(e){
				this.audioUrl = e
				if(e==""){
					uni.showToast({
						title:"音频生成失败",
						icon:"none"
					})
				}
			},
			playStart(){
				if(!this.isPlay){
					innerAudioContext.src = this.audioUrl
					innerAudioContext.play()
				}else{
					innerAudioContext.pause()
				}
			},
			// 提交
			submit(data){
				this.audioTitle = data
				if (this.audioTitle.trim() == '') {
					uni.showToast({
						title: '音频标题不能为空',
						icon: 'none'
					})
					return
				}
				this.uploadAudioFile(this.audioUrl);
			},
			// 音色上传
			uplodyinse(filePath){
				var _this = this;
				_this.$upload(filePath).then(res => {
					if (res.data.errno == 1) {
							this.getTiqu( res.data.img)
					} else {
						uni.hideLoading()
						uni.showToast({
							title: res.data.message,
							icon: 'none'
						})
					}
				}).catch(err => {
					console.log(err, 'err')
					uni.showToast({
						title: '出错了',
						icon: 'none'
					})
				})
			},
			// 提取音色
			getTiqu(url){
				uni.showLoading({
					title: '提取中',
					mask:true
				})
				MinorlanguagesTiqu({
					uid: uni.getStorageSync('userId'),
					voiceUrl:url,
					lan_id:this.timbreId,
					list_id:this.list_id
				}).then(res=>{
					uni.hideLoading()
					if(res.errno == 0){
						uni.showToast({
							title:"提取成功",
							icon:"none"
						})
						setTimeout(()=>{
							uni.navigateBack({
								delta:2,
							})
						},1000)
						console.log(res,"提取音色");
					}else {
						uni.showToast({
							title:res.msg,
							icon:"none"
						})
					}
					
					console.log(res,"提取音色");
				}).catch(err => {
					console.log(err, 'err')
					uni.hideLoading()
					uni.showToast({
						title: '提取失败',
						icon: 'none'
					})
				})
			},
			uploadAudioFile(filePath) {
				var _this = this;
				_this.isLoading = true;
				_this.$upload(filePath).then(res => {
					console.log(res, 'uploadImg')
					if (res.data.errno == 1) {
						uni.hideLoading();
						_this.audioUrl = res.data.img
						// _this.audioName = fileName
						console.log(_this.audioUrl, '上传音频')
			
						if (_this.toAddType == 1 || _this.toAddType == 3) {
							_this.addTimbreToMine();
						} else {
							_this.addAudioToMine();
						}
			
					} else {
						uni.hideLoading();
						uni.showToast({
							title: res.data.message,
							icon: 'none'
						})
					}
			
			
				}).catch(err => {
					uni.hideLoading();
					console.log(err, 'err')
			
				})
			},
			addTimbreToMine() {
				uni.showLoading({
					title: '上传中'
				})
				myYinseFileupNew({
					id: this.timbreId,
					title: this.audioTitle,
					mp3: this.audioUrl
				}).then(res => {
					console.log(res, 'ressss')
					
					if (res.errno == 0) {
						console.log(this.videoPath, 'this.videoPath')
						var data = res.data;
						setTimeout(()=>{
							this.editTimbreUploadSecond(data.id,data.uid,data.voice_name,data.title,data.mp3)
						},1000)
					} else {
						this.$refs.audiotitle.close();
						uni.showToast({
							title: res.msg,
							icon: 'none',
							duration: 1000
						})
						setTimeout(()=>{
							this.isLoading = false;
							uni.hideLoading();
							uni.reLaunch({
								url: '/pages/mine/index?current=1'
							})
						},1500)
						
					}
				}).catch(err => {
					this.$refs.audiotitle.close();
					console.log(err, 'err')
					uni.showToast({
						title: '出错了,请重新上传',
						icon: 'none',
						duration: 1000
					})
					setTimeout(()=>{
						this.isLoading = false;
						uni.hideLoading();
						uni.reLaunch({
							url: '/pages/mine/index?current=1'
						})
					},1500)
				})
			},
			editTimbreUploadSecond(id,uid,voice_name,title,mp3){
				uni.hideLoading();
				uni.showLoading({
					title: '语音生成中'
				})
				// var _this = this;
				morenShengYin({
					id: id,
					voice_name: voice_name,
					uid: uid,
					title: title,
					mp3: mp3
				}).then(res => {
					console.log(res, 'ressss')
					
					if (res.errno == 0) {
						uni.showToast({
							title: '成功',
							icon: 'none',
							durat: 1000
						})
						this.$refs.audiotitle.close();
						setTimeout(()=>{
							this.isLoading = false;
							uni.hideLoading();
							uni.reLaunch({
								url: '/pages/mine/index?current=1'
							})
						},1500)
					} else {
						uni.hideLoading()
						this.editTribreErrNum++;
						if(this.editTribreErrNum <5){
							uni.showLoading({
								title: '语音生成中'
							})
							setTimeout(()=>{
								this.editTimbreUploadSecond(id,uid,voice_name,title,mp3)
							},1000)
							
						}else {
							uni.showToast({
								title: res.msg,
								icon: 'none',
								durat: 1000
							})
							this.$refs.audiotitle.close();
							setTimeout(()=>{
								this.isLoading = false;
								uni.hideLoading();
								uni.reLaunch({
									url: '/pages/mine/index?current=1'
								})
							},1500)
						}
					}
				}).catch(err => {
					
					uni.showToast({
						title: res.msg,
						icon: 'none',
						durat: 1000
					})
					this.$refs.audiotitle.close();
					setTimeout(()=>{
						this.isLoading = false;
						uni.hideLoading();
						uni.reLaunch({
							url: '/pages/mine/index?current=1'
						})
					},1500)
				})
			},
			addAudioToMine() {
				uni.showLoading({
					title: '上传中'
				})
				this.isLoading = true;
				tapeFile({
					uid: uni.getStorageSync("userId"),
					title: this.audioTitle,
					mp3: this.audioUrl
				}).then(res => {
					console.log(res, 'eeee')
					this.isLoading = false;
					uni.hideLoading();
					if (res.errno == 0) {
						console.log(this.videoPath, 'this.videoPath')
						this.audioId = res.data;
						if (this.toAddType == 1) {
							uni.switchTab({
								url: '/pages/mine/index?current=1'
							})
						} else if (this.toAddType == 2) {
							if (this.toOptionType == 1) {
								uni.reLaunch({
									url: '/pages/create/index?videoPath=' + this.videoPath + '&audioUrl=' +
										this.audioUrl + '&audioTitle=' + this.audioTitle + '&audioId=' +
										this.audioId
								})
							} else if (this.toOptionType == 2) {
								uni.redirectTo({
									url: '/pagesHome/audioList/index?videoPath=' + this.videoPath
								})
							} else if (this.toOptionType == 3) {
								uni.navigateBack({delta:uni.getStorageSync("gateback")+1})
								// uni.reLaunch({
								// 	url: '/pagesMine/worksLibrary/index?current=1'
								// })
							}
						} else if (this.toAddType == 3) {
							uni.redirectTo({
								url: '/pagesCreate/addAudioByText/index?videoPath=' + this.videoPath +
									'&clickoption=' + this.clickoption + '&toOptionType=' + this
									.toOptionType
							})
						} else if (this.toAddType == 4) {
							if (this.toOptionType == 1) {
								uni.reLaunch({
									url: '/pages/create/index?videoPath=' + this.videoPath + '&audioUrl=' +
										this.audioUrl + '&audioTitle=' + this.audioTitle + '&audioId=' +
										this.audioId
								})
							} else if (this.toOptionType == 2) {
								uni.redirectTo({
									url: '/pagesHome/audioList/index?videoPath=' + this.videoPath
								})
							} else if (this.toOptionType == 3) {
								uni.navigateBack({delta:uni.getStorageSync("gateback")})
								// uni.reLaunch({
								// 	url: '/pagesMine/worksLibrary/index?current=1'
								// })
							}
						} else if (this.toAddType == 5) {
							if (this.toOptionType == 1) {
								uni.reLaunch({
									url: '/pages/create/index?videoPath=' + this.videoPath + '&audioUrl=' +
										this.audioUrl + '&audioTitle=' + this.audioTitle + '&audioId=' +
										this.audioId
								})
							} else if (this.toOptionType == 2) {
								uni.redirectTo({
									url: '/pagesHome/audioList/index?videoPath=' + this.videoPath
								})
							} else if (this.toOptionType == 3) {
								uni.navigateBack({delta:uni.getStorageSync("gateback")})
								// uni.reLaunch({
								// 	url: '/pagesMine/worksLibrary/index?current=1'
								// })
							}
							// uni.reLaunch({
							// 	url: '/pagesCreate/addAudioByText/index?videoPath=' + this.videoPath + '&clickoption=' + this.clickoption + '&toOptionType=' + this.toOptionType
							// })
						}
			
			
					}
				}).catch(err => {
					this.isLoading = false;
					uni.hideLoading();
				})
			},
			
			uniPage__onShow() { //页面onShow时【必须调用】的函数，传入当前组件this
				RecordApp.UniPageOnShow(this);
			},
			reclog(msg, color, set) {
				var now = new Date();
				var t = ("0" + now.getHours()).substr(-2) +
					":" + ("0" + now.getMinutes()).substr(-2) +
					":" + ("0" + now.getSeconds()).substr(-2);
				var txt = "[" + t + "]" + msg;
				if (!set || !set.noLog) console.log(txt);
			},
			currentKeyTag() {
				if (!RecordApp.Current) return "[?]";
				// #ifdef APP
				var tag2 = "Renderjs+H5";
				if (RecordApp.UniNativeUtsPlugin) {
					tag2 = RecordApp.UniNativeUtsPlugin.nativePlugin ? "NativePlugin" : "UtsPlugin";
				}
				return RecordApp.Current.Key + "(" + tag2 + ")";
				// #endif
				return RecordApp.Current.Key;
			},
			formatTime(ms, showSS) {
				var ss = ms % 1000;
				ms = (ms - ss) / 1000;
				var s = ms % 60;
				ms = (ms - s) / 60;
				var m = ms % 60;
				ms = (ms - m) / 60;
				var h = ms,
					v = "";
				if (h > 0) v += (h < 10 ? "0" : "") + h + ":";
				v += (m < 10 ? "0" : "") + m + ":";
				v += (s < 10 ? "0" : "") + s;
				// if (showSS) v += "″" + ("00" + ss).substr(-3);;
				return v;
			},
			
			//授权录音
			 startRec(){
				 this.$isAuthorize('scope.record',{title: '录音权限请求',content: '为了使用录音功能，我们需要您的麦克风权限。'}).then(res=> {
				 	console.log(res,'授权')
				 	if(res == true){
				 		// 已授权
				 		this.recStart()
				 	}else {
				 		// 未授权打开授权
				 	}
				 })
			 },
			
			// 开始录音
			recStart(){
				if(this.isPlay){
					innerAudioContext.stop()
				}
				
				console.log(this.$refs.player, 'this.$refs.player')
				this.$refs.player.setPlayBytes(null);
				this.takeoffEncodeChunkMsg = "";
				var takeEcCount = 0,
					takeEcSize = 0;
				this.takeEcChunks = this.takeoffEncodeChunkSet ? [] : null;
				
				if(this.useAEC){ //这个是Start中的audioTrackSet配置，在h5（H5、App+renderjs）中必须提前配置，因为h5中RequestPermission会直接打开录音
					RecordApp.RequestPermission_H5OpenSet={ audioTrackSet:{ noiseSuppression:true,echoCancellation:true,autoGainControl:true } };
				}
				
				this.recpowert = "0:00"
				// this.watchDogTimer=0; this.wdtPauseT=0; var processTime=0;
				
				this.reclog(this.currentKeyTag() + " 正在打开...");
				RecordApp.UniWebViewActivate(this); //App环境下必须先切换成当前页面WebView
				
				RecordApp.Start({
					type: this.recType,
					sampleRate: this.recSampleRate,
					bitRate: this.recBitRate,
					audioTrackSet: !this.useAEC ? null :
						{ //配置回声消除，H5、App、小程序均可用，但并不一定会生效；注意：H5、App+renderjs中需要在请求录音权限前进行相同配置RecordApp.RequestPermission_H5OpenSet后此配置才会生效
							noiseSuppression: true,
							echoCancellation: true,
							autoGainControl: true
						},
					onProcess:(buffers,powerLevel,duration,sampleRate,newBufferIdx,asyncEnd)=>{
						//全平台通用：可实时上传（发送）数据，配合Recorder.SampleData方法，将buffers中的新数据连续的转换成pcm上传，或使用mock方法将新数据连续的转码成其他格式上传，可以参考Recorder文档里面的：Demo片段列表 -> 实时转码并上传-通用版；基于本功能可以做到：实时转发数据、实时保存数据、实时语音识别（ASR）等
						
						//注意：App里面是在renderjs中进行实际的音频格式编码操作，此处的buffers数据是renderjs实时转发过来的，修改此处的buffers数据不会改变renderjs中buffers，所以不会改变生成的音频文件，可在onProcess_renderjs中进行修改操作就没有此问题了；如需清理buffers内存，此处和onProcess_renderjs中均需要进行清理，H5、小程序中无此限制
						//注意：如果你要用只支持在浏览器中使用的Recorder扩展插件，App里面请在renderjs中引入此扩展插件，然后在onProcess_renderjs中调用这个插件；H5可直接在这里进行调用，小程序不支持这类插件；如果调用插件的逻辑比较复杂，建议封装成js文件，这样逻辑层、renderjs中直接import，不需要重复编写
						
						this.recpowerx=powerLevel;
						// this.recpowert=this.formatTime(duration,1)+" / "+powerLevel;
						this.recpowert=this.formatTime(duration,1);
						// processTime=Date.now();
						
						//H5、小程序等可视化图形绘制，直接运行在逻辑层；App里面需要在onProcess_renderjs中进行这些操作
						// #ifdef H5 || MP-WEIXIN
						var wave=this.waveStore&&this.waveStore[this.recwaveChoiceKey];
						if(wave){
							wave.input(buffers[buffers.length-1],powerLevel,sampleRate);
						}
						// #endif
						//实时语音通话对讲，实时处理录音数据
						if(this.wsVoiceProcess) this.wsVoiceProcess(buffers,powerLevel,duration,sampleRate,newBufferIdx);
						
						//实时释放清理内存，用于支持长时间录音；在指定了有效的type时，编码器内部可能还会有其他缓冲，必须同时提供takeoffEncodeChunk才能清理内存，否则type需要提供unknown格式来阻止编码器内部缓冲，App的onProcess_renderjs中需要进行相同操作
						if(this.takeEcChunks){
							if(this.clearBufferIdx>newBufferIdx){ this.clearBufferIdx=0 } //重新录音了就重置
							for(var i=this.clearBufferIdx||0;i<newBufferIdx;i++) buffers[i]=null;
							this.clearBufferIdx=newBufferIdx;
						}
					},
					onProcess_renderjs: `function(buffers,powerLevel,duration,sampleRate,newBufferIdx,asyncEnd){
						this.recpowerx=powerLevel;
						this.recpowert=this.formatTime(duration,1)+" / "+powerLevel;
						//App中在这里修改buffers才会改变生成的音频文件
						//App中是在renderjs中进行的可视化图形绘制，因此需要写在这里，this是renderjs模块的this（也可以用This变量）；如果代码比较复杂，请直接在renderjs的methods里面放个方法xxxFunc，这里直接使用this.xxxFunc(args)进行调用
						var wave=this.waveStore&&this.waveStore[this.recwaveChoiceKey];
						if(wave){
							wave.input(buffers[buffers.length-1],powerLevel,sampleRate);
						}
						
						//和onProcess中一样进行释放清理内存，用于支持长时间录音
						if(${this.takeEcChunks?1:0}){
							if(this.clearBufferIdx>newBufferIdx){ this.clearBufferIdx=0 } //重新录音了就重置
							for(var i=this.clearBufferIdx||0;i<newBufferIdx;i++) buffers[i]=null;
							this.clearBufferIdx=newBufferIdx;
						}
					}`
						,
					takeoffEncodeChunk:!this.takeoffEncodeChunkSet?null:(chunkBytes)=>{
						//全平台通用：实时接收到编码器编码出来的音频片段数据，chunkBytes是Uint8Array二进制数据，可以实时上传（发送）出去
						//App中如果未配置RecordApp.UniWithoutAppRenderjs时，建议提供此回调，因为录音结束后会将整个录音文件从renderjs传回逻辑层，由于uni-app的逻辑层和renderjs层数据交互性能实在太拉跨了，大点的文件传输会比较慢，提供此回调后可避免Stop时产生超大数据回传
						takeEcCount++; takeEcSize+=chunkBytes.byteLength;
						this.takeoffEncodeChunkMsg="已接收到"+takeEcCount+"块，共"+takeEcSize+"字节";
						this.takeEcChunks.push(chunkBytes);
						
						//App中使用原生插件时，可方便的将数据实时保存到同一文件，第一帧时append:false新建文件，后面的append:true追加到文件
						//RecordApp.UniNativeUtsPluginCallAsync("writeFile",{path:"xxx.mp3",append:回调次数!=1, dataBase64:RecordApp.UniBtoa(chunkBytes.buffer)}).then(...).catch(...)
					},
					takeoffEncodeChunk_renderjs: !this.takeoffEncodeChunkSet ? null : `function(chunkBytes){
						//App中这里可以做一些仅在renderjs中才生效的事情，不提供也行，this是renderjs模块的this（也可以用This变量）
					}`
					,
					start_renderjs: `function(){
						//App中可以放一个函数，在Start成功时renderjs中会先调用这里的代码，this是renderjs模块的this（也可以用This变量）
						//放一些仅在renderjs中才生效的事情，比如初始化，不提供也行
					}`
					,
					stop_renderjs:`function(aBuf,duration,mime){
						//App中可以放一个函数，在Stop成功时renderjs中会先调用这里的代码，this是renderjs模块的this（也可以用This变量）
						this.audioData=aBuf; //留着给Stop时进行转码成wav播放
					}`
				},()=>{
					this.recordStatus = '录音中';
					// this.reclog(this.currentKeyTag() + " 录制中：" + this.recType +
					// 	" " + this.recSampleRate + " " + this.recBitRate + "kbps" +
					// 	(this.takeoffEncodeChunkSet ? " takeoffEncodeChunk" : "") +
					// 	(this.useAEC ? " useAEC" : "") +
					// 	(this.appUseH5Rec ? " appUseH5Rec" : ""), 2);
					setTimeout(()=>{
						//创建音频可视化图形绘制
						this.initWaveStore();
					},300)
					
					
				}, (msg) => {
					this.recordStatus = "未开始"
					this.recpowert = "0:00"
					uni.showToast({
						title:"录音失败",
						icon:"none"
					})
					this.reclog(this.currentKeyTag() + " 开始录音失败：" + msg, 1);
				})
			},
			// 暂停录音
			recPause(){
				if (RecordApp.GetCurrentRecOrNull()) {
					RecordApp.Pause();
					// this.reclog("已暂停");
					this.recordStatus = "已暂停"
				}
			},
			// 继续录音
			recResume(){
				if (RecordApp.GetCurrentRecOrNull()) {
					RecordApp.Resume();
					// this.reclog("继续录音中...");
					this.recordStatus = "录音中"
				}
			},
			// 清理录音
			recStopX(){
				RecordApp.Stop(
					null //success传null就只会清理资源，不会进行转码
					, (msg) => {
						this.recordStatus = "未开始"
						this.recpowert = "0:00"
						innerAudioContext.stop()
						this.audioUrl = ""
						console.log(msg,"紧清理资源");
					}
				);
			},
			//停止录音
			recStop(){
				this.reclog("正在结束录音...");
				RecordApp.Stop((aBuf, duration, mime) => {
					//全平台通用：aBuf是ArrayBuffer音频文件二进制数据，可以保存成文件或者发送给服务器
					//App中如果在Start参数中提供了stop_renderjs，renderjs中的函数会比这个函数先执行
					
					var recSet = (RecordApp.GetCurrentRecOrNull() || {
						set: {
							type: this.recType
						}
					}).set;
					this.reclog("已录制[" + mime + "]：" + this.formatTime(duration, 1) + " " + aBuf.byteLength +
						"字节 " +
						recSet.sampleRate + "hz " + recSet.bitRate + "kbps", 2);
				
					var aBuf_renderjs = "this.audioData";
					if (this.takeEcChunks) {
						aBuf_renderjs = ""; //renderjs的数据是空的
						this.reclog("启用takeoffEncodeChunk后Stop返回的blob长度为0不提供音频数据");
						var len = 0;
						for (var i = 0; i < this.takeEcChunks.length; i++) len += this.takeEcChunks[i].length;
						var chunkData = new Uint8Array(len);
						for (var i = 0, idx = 0; i < this.takeEcChunks.length; i++) {
							var itm = this.takeEcChunks[i];
							chunkData.set(itm, idx);
							idx += itm.length;
						};
						aBuf = chunkData.buffer;
						this.reclog("takeoffEncodeChunk接收到的音频片段，已合并成一个音频文件 " + aBuf.byteLength + "字节");
					}
					//用变量保存起来，别的地方调用
					this.lastRecType = recSet.type;
					this.lastRecBuffer = aBuf;
					
					//播放，部分格式会转码成wav播放
					this.$refs.player.setPlayBytes(aBuf, aBuf_renderjs, duration, mime, recSet, Recorder);
					this.recordStatus = "已结束"
				}, (msg) => {
					this.reclog("结束录音失败：" + msg, 1);
				});
				
			},
			// 创建波形
			initWaveStore(){
				//if(isWx && this.waveStore)return;//可以限制初始化一次；如果你的canvas所在的view存在v-if之类的会重新创建了对应的view，必须将波形重新进行初始化才能使用；如果使用的是同一个view，重新初始化后如果上次的动画没有完成时，小程序中开头部分新的波形会和老的波形相互干扰，老动画完成后恢复正常，App、H5无此问题
				var store=this.waveStore=this.waveStore||{};//这个测试demo会创建多个可视化，所以用个对象存起来，只有一个的时候请直接用个变量存一下即可 不用搞这么复杂
				var webStore=`var store=this.waveStore=this.waveStore||{};`;//在renderjs中执行，this是renderjs模块的this
				webStore+=`this.recwaveChoiceKey="${this.recwaveChoiceKey}";`;//把当前选中的波形也传过去
				//App环境下是在renderjs中绘制，H5、小程序等是在逻辑层中绘制，因此需要提供两段相同的代码（宽高值需要和canvas style的宽高一致）
				RecordApp.UniFindCanvas(this,[".recwave-Histogram3"],`${webStore}
					store.Histogram3=Recorder.FrequencyHistogramView({compatibleCanvas:canvas1, width:${this.rippleWid}, height:${this.rippleHei}
						,lineCount:50,position:0,minHeight:1,fallDuration:400,stripeEnable:false,mirrorEnable:true
						,linear:[0,"#fff",1,"#fff"]});
				`,(canvas1)=>{
					store.Histogram3=Recorder.FrequencyHistogramView({compatibleCanvas:canvas1, width:this.rippleWid, height:this.rippleHei
						,lineCount:50,position:0,minHeight:1,fallDuration:400,stripeEnable:false,mirrorEnable:true
						,linear:[0,"#fff",1,"#fff"]});
				});
			},
		}
	}
	
</script>
<!-- #ifdef APP -->
<script module="testMainVue" lang="renderjs">
	//此模块内部只能用选项式API风格，vue2、vue3均可用；不可改成setup组合式API风格，否则可能不能import vue导致编译失败
	/**============= App中在renderjs中引入RecordApp，这样App中也能使用H5录音、音频可视化 =============**/
	/** 先引入Recorder **/
	import Recorder from 'recorder-core'; //注意如果未引用Recorder变量，可能编译时会被优化删除（如vue3 tree-shaking），请改成 import 'recorder-core'，或随便调用一下 Recorder.a=1 保证强引用

	//按需引入需要的录音格式编码器，用不到的不需要引入，减少程序体积；H5、renderjs中可以把编码器放到static文件夹里面用动态创建script来引入，免得这些文件太大
	import 'recorder-core/src/engine/mp3.js'
	import 'recorder-core/src/engine/mp3-engine.js'
	// import 'recorder-core/src/engine/wav.js'
	// import 'recorder-core/src/engine/pcm.js'
	// import 'recorder-core/src/engine/g711x'
	// import 'recorder-core/src/engine/beta-amr'
	// import 'recorder-core/src/engine/beta-amr-engine'
	// import 'recorder-core/src/engine/beta-ogg'
	// import 'recorder-core/src/engine/beta-ogg-engine'

	//可选引入可视化插件
	import 'recorder-core/src/extensions/waveview.js'
	import 'recorder-core/src/extensions/wavesurfer.view.js'

	import 'recorder-core/src/extensions/frequency.histogram.view.js'
	import 'recorder-core/src/extensions/lib.fft.js'

	//实时播放语音，仅支持h5
	// import 'recorder-core/src/extensions/buffer_stream.player.js'
	//测试用根据简谱生成一段音乐
	// import 'recorder-core/src/extensions/create-audio.nmn2pcm.js'

	/** 引入RecordApp **/
	import RecordApp from 'recorder-core/src/app-support/app.js'
	//【必须引入】uni-app支持文件
	import '../../uni_modules/Recorder-UniCore/app-uni-support.js'

	export default {
		mounted() {
			//App的renderjs必须调用的函数，传入当前模块this
			RecordApp.UniRenderjsRegister(this);

			this.$ownerInstance.callMethod("reclog", "renderjs mounted" +
				"，WebViewId=" + window.__WebVieW_Id__ +
				"，ComponentId=_$id:" + (this._$id || "?") + "/$vm.ownerId:" + (this.$ownerInstance.$vm.ownerId || "?"));
		},
		methods: {
			//这里定义的方法，在逻辑层中可通过 RecordApp.UniWebViewVueCall(this,'this.xxxFunc()') 直接调用
			//调用逻辑层的方法，请直接用 this.$ownerInstance.callMethod("xxxFunc",{args}) 调用，二进制数据需转成base64来传递
		}
	}
</script>
<!-- #endif -->
<style lang="less" scoped>
	/deep/ .u-mode-center-box {
		background-color: rgba(0, 0, 0, 0) !important;
	}
</style>
<style>
	.page{
		background-color: #111113;
		height: 100vh;
		padding: 0 30rpx;
		box-sizing: border-box;
	}
	.image_5 {
		width: 106rpx;
		height: 106rpx;
	}
	.image_6 {
		border-radius: 34rpx;
		width: 108rpx;
		height: 68rpx;
	}
	.top{
		height: calc(100% - 550rpx - 95px);
		margin-bottom: 50rpx;
		padding: 48rpx 32rpx;
		box-sizing: border-box;
		border-radius: 32rpx;
		background-image: url('https://terminator.axu9.com/images/add-text-bg.png');
		background-size: 100% 100%;
		background-repeat: no-repeat;
	}
	.title{
		color: #ffffff;
		font-size: 32rpx;
		font-family: PingFang SC;
		height: 80rpx;
	}
	.inpcon{
		width: 100%;
		height: calc(100% - 80rpx);
	}
	.textarea-class {
		/* height: 100%; */
		width: 100%;
		height: 100%;
		/* font-size: 26rpx; */
		font-size: 32rpx;
		font-family: PingFang SC;
		color: #ffffff;
		line-height: 60rpx;
	}
	.font {
		font-size: 26rpx;
		font-family: PingFang SC;
		/* line-height: 80rpx; */
		color: #ffffff;
	}
	
	
	.bottom{
		height: 440rpx;
	}
	.b_btn{
		width: 100%;
		display: flex;
		flex-direction: column;
		justify-content: center;
		align-items: center;
		position: relative;
		z-index: 6;
	}
	.btnall{
		/* width: 100%; */
		display: flex;
		align-items: center;
	}
	.btn_con{
		margin: 0 52rpx;
	}
	.btn_text{
		font-size: 26rpx;
		font-family: PingFang SC;
		color: #ffffff;
		margin-top: 16rpx;
	}
	.ripple{
		background-color: #020f14;
		border: 1rpx solid #8f9192;
		border-radius: 32rpx;
		height:250rpx;
		width:100%;
		position: relative;
		margin-bottom: 24rpx;
	}
	.min{
		position: absolute;
		left: 50%;
		top: 30rpx;
		transform: translate(-50%);
		color: #fff;
		display: flex;
		align-items: center;
		font-size: 24rpx;
	}
	.recwave-Histogram3{
		position: absolute;
		left:0;
		bottom: 0;
		height:100%;
		width:100%;
	}
	.image_4{
		width: 100%;
		height: 56rpx;
		/* position: absolute; */
		/* left: 50%; */
		/* top: 45%; */
		/* transform: translate(-50%); */
	}
	.image_4gif{
		height: 100rpx;
		/* top: calc(45% - 24rpx); */
	}
	
	.image_3 {
		width: 32rpx;
		height: 38rpx;
		margin-right: 16rpx;
	}
</style>